package directory

import (
	"context"
	"fmt"
	"strings"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/service/attachment/model"
	"mall/service/attachment/rpc/internal/svc"
	"mall/service/attachment/rpc/internal/utils/errorhandler"
	"mall/service/attachment/rpc/types/attachment"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"

	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type BackendBatchUpdateRecordLogic struct {
	ctx           context.Context
	svcCtx        *svc.ServiceContext
	bucketManager *storage.BucketManager
	bucket        string
	logx.Logger
}

func NewBackendBatchUpdateRecordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BackendBatchUpdateRecordLogic {
	return &BackendBatchUpdateRecordLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *BackendBatchUpdateRecordLogic) BackendBatchUpdateRecord(in *attachment.RecordListReq) (*attachment.BaseResp, error) {
	if in.Ids == nil || len(strings.TrimSpace(*in.Ids)) == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	var diagnose uint32
	if in.Diagnose != nil {
		diagnose = *in.Diagnose
	}

	if diagnose == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	mac := qbox.NewMac(l.svcCtx.Config.QiniuConf.AccessKey, l.svcCtx.Config.QiniuConf.SecretKey)

	cfg := storage.Config{
		UseHTTPS:      l.svcCtx.Config.QiniuConf.UseHTTPS, // 是否使用https域名进行资源管理
		UseCdnDomains: l.svcCtx.Config.QiniuConf.UseCdnDomains, // 上传是否使用CDN上传加速
	}
	// 指定空间所在的区域，如果不指定将自动探测
	// 如果没有特殊需求，默认不需要指定
	// cfg.Region=&storage.ZoneHuabei
	switch l.svcCtx.Config.QiniuConf.Region {
	case "z2":
		cfg.Region = &storage.ZoneHuanan
	}

	l.bucketManager = storage.NewBucketManager(mac, &cfg)
	l.bucket = l.svcCtx.Config.QiniuConf.Bucket

	var err error
	switch diagnose {
	case 11: // 批量移动或重命名文件，如果目标文件存在，不强制覆盖，如果不覆盖，默认返回614 file exists
		err = l.qiniuMove(in, false)
	case 12: // 批量移动或重命名文件，如果目标文件存在，强制覆盖
		err = l.qiniuMove(in, true)
	case 13: // 批量复制文件，如果目标文件存在，不强制覆盖，如果不覆盖，默认返回614 file exists
		err = l.qiniuCopy(in, false)
	case 14: // 批量复制文件，如果目标文件存在，强制覆盖
		err = l.qiniuCopy(in, true)
	case 15: // 批量更新文件的有效期
		err = l.qiniuDeleteAfterDays(in)
	case 16: // 批量删除文件
		err = l.qiniuDelete(in)
	}

	if err != nil {
		return nil, err
	}

	return &attachment.BaseResp{
		Msg: i18n.UpdateSuccess,
	}, nil
}

func (l *BackendBatchUpdateRecordLogic) getList(
	in *attachment.RecordListReq,
) ([]*model.AttachmentFileInfo, error) {
	var orderBys []string
	if len(in.OrderBy) > 0 {
		for _, orderBy := range in.OrderBy {
			var order string = " ASC"
			if orderBy.Order == "1" {
				order = " DESC"
			}

			switch orderBy.Field {
			case "id":
				orderBys = append(orderBys, model.AttachmentFileInfoColumns.ID + order)
			}
		}
	}

	var stati string
	if len(in.FilterBy) > 0 {
		for _, filterBy := range in.FilterBy {
			if filterBy.Field == "status" {
				stati = strings.TrimSpace(filterBy.In)
			}
		}
	}

	var page uint64
	var pageSize uint64
	var restricted bool
	filterMap := make(map[string]map[string][]any)
	if in.Ids != nil {
		if vals := modelx.UniqueInterfaces(strings.TrimSpace(*in.Ids), 64); len(vals) > 0 {
			filterMap[model.AttachmentFileInfoColumns.ID] = map[string][]any{
				"in": vals,
			}
			restricted = true
		}
	}
	if !restricted {
		page = in.Page
		pageSize = in.PageSize
	}

	statusIn := modelx.UniqueInterfaces(stati, 32)
	statusMap := make(map[string][]any)
	if len(statusIn) > 0 {
		statusMap["in"] = statusIn
	}
	if in.Status != nil && *in.Status > 0 {
		statusMap["equalTo"] = []any{*in.Status - 1}
	}
	filterMap[model.AttachmentFileInfoColumns.DelStatus] = statusMap

	var orderBy string
	if len(orderBys) > 0 {
		orderBy = strings.Join(orderBys, ",")
	}
	list, _, err := l.svcCtx.FileInfo.GetList(l.ctx, model.M{
		"page":     page,
		"pageSize": pageSize,
		"orderBy":  orderBy,
	}, -1, filterMap)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	return list, err
}

func (l *BackendBatchUpdateRecordLogic) qiniuMove(in *attachment.RecordListReq, force bool) error {
	// todo
	return nil
}

func (l *BackendBatchUpdateRecordLogic) qiniuCopy(in *attachment.RecordListReq, force bool) error {
	// todo
	return nil
}

func (l *BackendBatchUpdateRecordLogic) qiniuDeleteAfterDays(in *attachment.RecordListReq) error {
	list, err := l.getList(in)
	if err != nil {
		return err
	}

	var days int
	if in.Days != nil && *in.Days > 0 {
		days = int(*in.Days)
	} else {
		days = 7
	}

	// 每个batch的操作数量不可以超过1000个，如果总数量超过1000，需要分批发送
	expireOps := make([]string, 0, len(list))
	statOps := make([]string, 0, len(list))
	hashMap := make(map[string][]*model.AttachmentFileInfo)
	for _, v := range list {
		expireOps = append(expireOps, storage.URIDeleteAfterDays(l.bucket, v.KeyValue, days))
		statOps = append(statOps, storage.URIStat(l.bucket, v.KeyValue))
		hashMap[v.Hash] = append(hashMap[v.Hash], v)
	}

	rets, err := l.bucketManager.Batch(expireOps)
	if len(rets) == 0 {
		// 处理错误
		if e, ok := err.(*storage.ErrorInfo); ok {
			logrus.Info(fmt.Sprintf("Qiniu batch DeleteAfterDays detailed err: %+v", e))
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch DeleteAfterDays err: %+v", err))
		}

		return errorx.NewInternalError("file.listFilesFailed")
	}

	for _, ret := range rets {
		// 200 为成功
		if ret.Code == 200 {
			logrus.Info(fmt.Sprintf("Qiniu batch DeleteAfterDays: %+v", ret))
			// {Code:200 Data:{Fsize:0 Hash: MimeType: Type:0 PutTime:0 RestoreStatus:<nil> Status:<nil> Md5: EndUser: Expiration:<nil> TransitionToIA:<nil> TransitionToArchive:<nil> TransitionToDeepArchive:<nil> Error:}}
			// {Code:200 Data:{Fsize:0 Hash: MimeType: Type:0 PutTime:0 RestoreStatus:<nil> Status:<nil> Md5: EndUser: Expiration:<nil> TransitionToIA:<nil> TransitionToArchive:<nil> TransitionToDeepArchive:<nil> Error:}}
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch DeleteAfterDays err: %+v", ret.Data.Error))

			return errorx.NewInternalError("file.listFilesFailed")
		}
	}

	rs, err := l.bucketManager.Batch(statOps)
	if len(rs) == 0 {
		// 处理错误
		if e, ok := err.(*storage.ErrorInfo); ok {
			logrus.Info(fmt.Sprintf("Qiniu batch Stat detailed err: %+v", e))
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch Stat err: %+v", err))
		}

		return errorx.NewInternalError("file.listFilesFailed")
	}

	for _, ret := range rs {
		// 200 为成功
		if ret.Code == 200 {
			logrus.Info(fmt.Sprintf("Qiniu batch Stat: %+v", ret))
			// {Code:200 Data:{Fsize:163469 Hash:FpHyF0kkil3sp-SaXXX8TBJY3jDh MimeType:image/png Type:0 PutTime:16977028868369054 RestoreStatus:<nil> Status:<nil> Md5:caf44df8c885981e8a8c55391f3096b6 EndUser: Expiration:0xc000a4c038 TransitionToIA:<nil> TransitionToArchive:<nil> TransitionToDeepArchive:<nil> Error:}}
			// {Code:200 Data:{Fsize:18262 Hash:FtzuVTH1TamwiPLoMZbdt6HEAQRZ MimeType:image/jpeg Type:0 PutTime:16977040577415497 RestoreStatus:<nil> Status:<nil> Md5:ad2129e7426db96182f77ce4da31e63c EndUser: Expiration:0xc000a4c060 TransitionToIA:<nil> TransitionToArchive:<nil> TransitionToDeepArchive:<nil> Error:}}
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch Stat err: %+v", ret.Data.Error))

			return errorx.NewInternalError("file.listFilesFailed")
		}
	}

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	for _, ret := range rs {
		if entries, ok := hashMap[ret.Data.Hash]; ok {
			for _, entry := range entries {
				entry.Size = uint64(ret.Data.Fsize)
				entry.MimeType = ret.Data.MimeType
				entry.Type = uint8(ret.Data.Type)
				entry.PutTime = uint64(ret.Data.PutTime)
				if ret.Data.RestoreStatus != nil {
					entry.RestoreStatus = uint8(*ret.Data.RestoreStatus)
				}
				if ret.Data.Status != nil {
					entry.Status = uint8(*ret.Data.Status)
				}
				entry.MD5 = ret.Data.Md5
				entry.EndUser = ret.Data.EndUser
				if ret.Data.Expiration != nil {
					entry.Expiration = uint64(*ret.Data.Expiration)
				}
				if ret.Data.TransitionToIA != nil {
					entry.TransitionToIa = uint64(*ret.Data.TransitionToIA)
				}
				if ret.Data.TransitionToArchive != nil {
					entry.TransitionToArchive = uint64(*ret.Data.TransitionToArchive)
				}
				if ret.Data.TransitionToDeepArchive != nil {
					entry.TransitionToDeepArchive = uint64(*ret.Data.TransitionToDeepArchive)
				}
				entry.DelStatus = uint8(globalkey.StatusDeleted)

				err := l.svcCtx.FileInfo.Update(l.ctx, tx, entry)
				if err != nil {
					tx.Rollback()
					return errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}
			}
		}
	}

	tx.Commit()

	return nil
}

func (l *BackendBatchUpdateRecordLogic) qiniuDelete(in *attachment.RecordListReq) error {
	list, err := l.getList(in)
	if err != nil {
		return err
	}

	// 每个batch的操作数量不可以超过1000个，如果总数量超过1000，需要分批发送
	deleteOps := make([]string, 0, len(list))
	for _, v := range list {
		deleteOps = append(deleteOps, storage.URIDelete(l.bucket, v.KeyValue))
	}

	rets, err := l.bucketManager.Batch(deleteOps)
	if len(rets) == 0 {
		// 处理错误
		if e, ok := err.(*storage.ErrorInfo); ok {
			logrus.Info(fmt.Sprintf("Qiniu batch Delete detailed err: %+v", e))
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch Delete err: %+v", err))
			// Qiniu batch Stat err: no such file or directory
		}

		return errorx.NewInternalError("file.listFilesFailed")
	}

	for _, ret := range rets {
		// 200 为成功
		if ret.Code == 200 {
			logrus.Info(fmt.Sprintf("Qiniu batch Delete: %+v", ret))
			//
		} else {
			logrus.Info(fmt.Sprintf("Qiniu batch Delete err: %+v", ret.Data.Error))

			return errorx.NewInternalError("file.listFilesFailed")
		}
	}

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	for _, entry := range list {
		err = l.svcCtx.FileInfo.SoftDelete(l.ctx, tx, entry.ID)
		if err != nil {
			tx.Rollback()
			return errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	}

	tx.Commit()

	return nil
}
